package com.dc.aimc.auth.entity;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.ClientDetails;

import java.util.*;

/**
 * <p>
 *  自定义客户端详情类
 * </p>
 *
 * @author 冯照龙
 * @since 2021-12-24
 */
@Data
@TableName("oauth_client_details")
public class OauthClientDetails implements ClientDetails {

    private static final long serialVersionUID = 1L;

    /**
     * 唯一标识每一个客户端
     */
    @TableId(value = "client_id", type = IdType.INPUT)
    private String clientId;

    /**
     * 客户端(client)的访问密匙
     */
    @TableField("client_secret")
    private String clientSecret;

    /**
     * 所能访问的资源id集合,多个资源时用逗号(,)分隔
     */
    @TableField("resource_ids")
    private String resourceIds;

    /**
     * 客户端支持的授权类型,可选值包括authorization_code,password,refresh_token,implicit,client_credentials, 若支持多个grant_type用逗号(,)
     */
    @TableField("authorized_grant_types")
    private String authorizedGrantTypes;

    /**
     * 客户端的重定向URI,可为空
     */
    @TableField(value = "web_server_redirect_uri")
    private String webServerRedirectUri;

    /**
     * 是否自动Approval操作, 默认值为 'false', 可选值包括 'true','false', 'read','write' 只适用于grant_type="authorization_code"的情况
     */
    @TableField(value = "auto_approve")
    private String autoApprove;

    /**
     * 客户端的access_token的有效时间值(单位:秒),可选,默认值(60 * 60 * 12)12小时
     */
    @TableField("access_token_validity")
    private Integer accessTokenValidity;

    /**
     * 客户端的refresh_token的有效时间值(单位:秒),可选,默认值(60 * 60 * 24 * 30)30天
     */
    @TableField("refresh_token_validity")
    private Integer refreshTokenValidity;

    /**
     * 客户端申请的权限范围,可选值包括read,write,trust.多个权限范围用逗号(,)分隔
     */
    @TableField(value = "scope")
    private String scope;

    /**
     * 客户端所拥有的SpringSecurity的权限值,可选,若有多个权限值用逗号(,)分隔
     */
    @TableField(value = "authorities")
    private String authorities;

    /**
     * 预留字段,没有实际的使用,若设置值,必须是JSON格式的数据
     */
    @TableField("additional_information")
    private String additionalInformation;

    /**
     * 标识客户端是否已存档(即实现逻辑删除),默认值为'0'(即未存档)
     */
    @TableField("archived")
    private Boolean archived;

    /**
     * 客户端是否为受信任的,默认为'0'(即不受信任的,1为受信任的) 只适用于grant_type="authorization_code"的情况
     */
    @TableField("trusted")
    private Boolean trusted;

    /**
     * The client id.
     *
     * @return The client id.
     */
    @Override
    public String getClientId() {
        return clientId;
    }

    /**
     * The resources that this client can access. Can be ignored by callers if empty.
     *
     * @return The resources of this client.
     */
    @Override
    public Set<String> getResourceIds() {
        return new HashSet<>(Arrays.asList(resourceIds.split(",")));
    }

    /**
     * Whether a secret is required to authenticate this client.
     *
     * @return Whether a secret is required to authenticate this client.
     */
    @Override
    public boolean isSecretRequired() {
        return clientSecret != null;
    }

    /**
     * The client secret. Ignored if the {@link #isSecretRequired() secret isn't required}.
     *
     * @return The client secret.
     */
    @Override
    public String getClientSecret() {
        return clientSecret;
    }

    /**
     * Whether this client is limited to a specific scope. If false, the scope of the authentication request will be
     * ignored.
     *
     * @return Whether this client is limited to a specific scope.
     */
    @Override
    public boolean isScoped() {
        return StringUtils.isNotEmpty(scope);
    }

    /**
     * The scope of this client. Empty if the client isn't scoped.
     *
     * @return The scope of this client.
     */
    @Override
    public Set<String> getScope() {
        return new HashSet<>(Arrays.asList(scope.split(",")));
    }

    /**
     * The grant types for which this client is authorized.
     *
     * @return The grant types for which this client is authorized.
     */
    @Override
    public Set<String> getAuthorizedGrantTypes() {
        return new HashSet<>(Arrays.asList(authorizedGrantTypes.split(",")));
    }

    /**
     * The pre-defined redirect URI for this client to use during the "authorization_code" access grant. See OAuth spec,
     * section 4.1.1.
     *
     * @return The pre-defined redirect URI for this client.
     */
    @Override
    public Set<String> getRegisteredRedirectUri() {
        return new HashSet<>(Arrays.asList(webServerRedirectUri.split(",")));
    }

    /**
     * Returns the authorities that are granted to the OAuth client. Cannot return <code>null</code>.
     * Note that these are NOT the authorities that are granted to the user with an authorized access token.
     * Instead, these authorities are inherent to the client itself.
     *
     * @return the authorities (never <code>null</code>)
     */
    @Override
    public Collection<GrantedAuthority> getAuthorities() {
        HashSet<GrantedAuthority> set = new HashSet<>();
        Arrays.stream(authorities.split(",")).forEach(a -> set.add(new SimpleGrantedAuthority(a)));
        return set;
    }

    /**
     * The access token validity period for this client. Null if not set explicitly (implementations might use that fact
     * to provide a default value for instance).
     *
     * @return the access token validity period
     */
    @Override
    public Integer getAccessTokenValiditySeconds() {
        return accessTokenValidity;
    }

    /**
     * The refresh token validity period for this client. Null for default value set by token service, and
     * zero or negative for non-expiring tokens.
     *
     * @return the refresh token validity period
     */
    @Override
    public Integer getRefreshTokenValiditySeconds() {
        return refreshTokenValidity;
    }

    /**
     * Test whether client needs user approval for a particular scope.
     *
     * @param scope the scope to consider
     * @return true if this client does not need user approval
     */
    @Override
    public boolean isAutoApprove(String scope) {
        if (autoApprove == null) {
            return false;
        }
        for (String auto : autoApprove.split(",")) {
            if (auto.equals("true") || scope.matches(auto)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Additional information for this client, not needed by the vanilla OAuth protocol but might be useful, for example,
     * for storing descriptive information.
     *
     * @return a map of additional information
     */
    @Override
    public Map<String, Object> getAdditionalInformation() {
        return JSON.parseObject(additionalInformation, new TypeReference<Map<String, Object>>(){});
    }


}
